﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace yafc.UI.Components
{
	public partial class ListBox : Panel
	{
		private List<ListBoxItem> items = new List<ListBoxItem>();
		private List<ListBoxItem> selection = new List<ListBoxItem>();

		private Comparison<ListBoxItem> comparison;

		private int capacity = -1;
		private bool multiselect = true;
		private bool ctrl = false;

		private ListBoxItem viewTopItem = null;
		private ListBoxItem viewBottomItem = null;

		public ListBox()
		{
			InitializeComponent();
		}

		public Comparison<ListBoxItem> Comparison
		{
			get { return comparison; }
			set { comparison = value; }
		}
		public ListBoxItem TopItem
		{
			get
			{
				return viewTopItem;
			}
			set
			{
				lock(items)
				{
					int y = 0;
					foreach (var i in items)
					{
						if (i == value)
						{
							break;
						}
						y += i.Height;
					}
					AutoScrollPosition = new Point(AutoScrollPosition.X, y);
				}
			}
		}
		public ListBoxItem BottomItem
		{
			get { return viewBottomItem; }
		}

		public int Capacity
		{
			get { return capacity; }
			set
			{
				capacity = value;
				Truncate();
			}
		}
		public bool MultiSelect
		{
			get { return multiselect; }
			set
			{
				multiselect = value;
				if (!multiselect && Selection.Count() > 1)
				{
					UnselectAll();
				}
			}
		}
		public List<ListBoxItem> Selection
		{
			get { return selection; }
		}

		public void Add(ListBoxItem Item)
		{
			lock(items)
			{
				items.Add(Item);
			}
			if (Item.Parent != this)
			{
				lock(Controls)
				{
					Controls.Add(Item);
				}
			}
			Item.MouseClick += OnItemMouseClick;
			Sort();
			Truncate();
		}
		public void Remove(ListBoxItem Item)
		{
			lock (items)
			{
				items.Add(Item);
			}
			lock (Controls)
			{
				Controls.Add(Item);
			}
			lock (selection)
			{
				Item.Selected = false;
				Selection.Remove(Item);
			}
			Item.MouseClick -= OnItemMouseClick;
		}
		public void Sort()
		{
			if (Comparison != null)
			{
				lock(items)
				{
					items.Sort(Comparison);
				}
			}
		}

		public void Select(ListBoxItem Item)
		{
			if (!Item.Selected)
			{
				Item.Selected = true;
				selection.Add(Item);
			}
		}
		public void Unselect(ListBoxItem Item)
		{
			if (Item.Selected)
			{
				Item.Selected = false;
				selection.Remove(Item);
			}
		}
		public void UnselectAll()
		{
			foreach (var i in Selection)
			{
				i.Selected = false;
			}
			Selection.Clear();
		}

		protected void Truncate()
		{
			if (Capacity < 0 || items.Count <= Capacity)
				return;

			var tmp = new List<ListBoxItem>();
			lock(items)
			{
				for (int i = Capacity; i < items.Count(); ++i )
				{
					tmp.Add(items[i]);
				}
				items.RemoveRange(Capacity, items.Count - Capacity);
			}
			lock(Controls)
			{
				foreach (var i in tmp)
				{
					Controls.Remove(i);
				}
			}
			lock (selection)
			{
				foreach (var i in tmp)
				{
					Selection.Remove(i);
				}				
			}
		}
		protected void OnItemMouseClick(object sender, MouseEventArgs e)
		{
			Focus();

			ListBoxItem item = sender as ListBoxItem;
			if (item == null)
				throw new Exception("Object not ListBoxItem found in ListBox!");

			if (e.Button == MouseButtons.Left)
			{
				LButtonClick(item);
			}
		}

		private void LButtonClick(ListBoxItem item)
		{
			if (!MultiSelect || !ctrl)
			{
				if (item.Selected)
				{
					UnselectAll();
				}
				else
				{
					UnselectAll();
					Select(item);
				}
			}
			else
			{
				if (item.Selected)
				{
					Unselect(item);
				}
				else
				{
					Select(item);
				}
			}
		}
		private void ListBox_Layout(object sender, LayoutEventArgs e)
		{
			if (items.Count == 0)
			{
				viewTopItem = null;
				viewBottomItem = null;
				return;
			}

			int x = 0;
			int y = AutoScrollPosition.Y;
			lock (items)
			{
				for (int i = 0; i < items.Count(); ++i )
				{
					var item = items.ElementAt(i);
					item.Location = new Point(x, y);
					item.Width = ClientSize.Width;
					item.PerformLayout();
					y += item.Height;

					if (y > 0)
						viewTopItem = item;
					else if (y > ClientSize.Height && i > 0)
						viewBottomItem = items.ElementAt(i-1);
				}
			}
			AutoScrollMinSize = new Size(ClientSize.Width, y);
		}
		private void ListBox_Scroll(object sender, ScrollEventArgs e)
		{
			int x = 0;
			int y = AutoScrollPosition.Y;
			lock (items)
			{
				foreach (var i in items)
				{
					i.Location = new Point(x, y);
					if (y + i.Height > 0 && y < ClientSize.Height)
					{
						i.Invalidate();
					}
					y += i.Height;
				}
			}
		}

		protected override void OnMouseWheel(MouseEventArgs e)
		{
			var ti = TopItem;
			if (ti != null)
			{
				var max_height = (int)(ti.Height * 0.875);
				if (Math.Abs(e.Delta) > max_height)
				{
					e = new MouseEventArgs(e.Button, e.Clicks, e.X, e.Y,
						e.Delta > 0 ? max_height : -max_height);
				}
			}
			base.OnMouseWheel(e);
		}
	}
}
